#include "meshTypeFunctions.hpp"
#include "structMesh_testFunctions.hpp"

#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>

using Catch::Approx;

#define MAKE_MESH(M,N,DX,DY) auto mesh { uniMeshAs<StructMesh>(M,N,DX,DY) };
#define MESH_PREFIX "Struct"
#define MESH_TYPE hms::StructMesh

#include "structMeshTestHelper.hpp"


TEST_CASE( "Non-rectangular StructMesh: number of edges in different regions" ){
	using hms::exactlyEqual;
	
	auto test = []( const auto& mesh ){
		REQUIRE( mesh.nInnerEdges()    == 12 );
		REQUIRE( mesh.nBoundaryEdges() == 12 );
		REQUIRE( mesh.nEdges()         == 24 );
	};

	test(createStructMesh());
}

TEST_CASE( "Non-rectangular StructMesh: number of cells of different kinds" ){

	using hms::exactlyEqual;

	auto testOneLayer = [](const auto& mesh){
		REQUIRE( mesh.nDomainCells() ==  9 );
		REQUIRE( mesh.nGhostCells () ==  12 );
		REQUIRE( mesh.nCells      () ==  21 );
	};

	testOneLayer(createStructMesh());
}

TEST_CASE( "Non-rectangular StructMesh: vertices and area" ){
	auto test = []( const auto& mesh ){
		REQUIRE( mesh.nVertices() == 16 );
		REQUIRE( mesh.domainArea() ==  Approx(7.34) );
		
		REQUIRE( approxEqual( mesh.vertex( 0), Vector2s{ 0.0, 0.0 } ) );
		REQUIRE( approxEqual( mesh.vertex( 1), Vector2s{ 1.0, 0.4 } ) );
		REQUIRE( approxEqual( mesh.vertex( 2), Vector2s{ 1.8, 0.2 } ) );
		REQUIRE( approxEqual( mesh.vertex( 3), Vector2s{ 2.8, 0.1 } ) );
		REQUIRE( approxEqual( mesh.vertex( 4), Vector2s{ 0.0, 0.8 } ) );
		REQUIRE( approxEqual( mesh.vertex( 5), Vector2s{ 1.0, 1.6 } ) );
		REQUIRE( approxEqual( mesh.vertex( 6), Vector2s{ 2.4, 1.6 } ) );
		REQUIRE( approxEqual( mesh.vertex( 7), Vector2s{ 2.6, 1.2 } ) );
		REQUIRE( approxEqual( mesh.vertex( 8), Vector2s{ 0.2, 2.2 } ) );
		REQUIRE( approxEqual( mesh.vertex( 9), Vector2s{ 1.2, 1.8 } ) );
		REQUIRE( approxEqual( mesh.vertex(10), Vector2s{ 1.8, 2.0 } ) );
		REQUIRE( approxEqual( mesh.vertex(11), Vector2s{ 3.2, 2.0 } ) );
		REQUIRE( approxEqual( mesh.vertex(12), Vector2s{ 0.0, 2.8 } ) );
		REQUIRE( approxEqual( mesh.vertex(13), Vector2s{ 1.0, 2.8 } ) );
		REQUIRE( approxEqual( mesh.vertex(14), Vector2s{ 2.2, 2.8 } ) );
		REQUIRE( approxEqual( mesh.vertex(15), Vector2s{ 3.2, 2.8 } ) );
	};

	test(createStructMesh());
}

TEST_CASE( "Non-rectangular StructMesh: boundary patches" ){
	using hms::exactlyEqual;
	auto testOneLayer = [](auto& mesh){
		hms::addStandardBoundaryPatches(mesh);

		REQUIRE(exactlyEqual(mesh.boundaryPatch("bottom").edges(),  Array3i{ 0,  1,  2}) );
		REQUIRE(exactlyEqual(mesh.boundaryPatch("top"   ).edges(),  Array3i{ 9, 10, 11}) );
		REQUIRE(exactlyEqual(mesh.boundaryPatch("left"  ).edges(),  Array3i{12, 16, 20}) );
		REQUIRE(exactlyEqual(mesh.boundaryPatch("right" ).edges(),  Array3i{15, 19, 23}) );

		REQUIRE(exactlyEqual(mesh.boundaryPatch("bottom").ghostCells(), Array3i{ 1, 2, 3} ) );
		REQUIRE(exactlyEqual(mesh.boundaryPatch("top"   ).ghostCells(), Array3i{21,22,23} ) );
		REQUIRE(exactlyEqual(mesh.boundaryPatch("left"  ).ghostCells(), Array3i{ 5,10,15} ) );
		REQUIRE(exactlyEqual(mesh.boundaryPatch("right" ).ghostCells(), Array3i{ 9,14,19} ) );

		REQUIRE(exactlyEqual(mesh.boundaryPatch("bottom").domainCells(), Array3i{ 6, 7, 8} ) );
		REQUIRE(exactlyEqual(mesh.boundaryPatch("top"   ).domainCells(), Array3i{16,17,18} ) );
		REQUIRE(exactlyEqual(mesh.boundaryPatch("left"  ).domainCells(), Array3i{ 6,11,16} ) );
		REQUIRE(exactlyEqual(mesh.boundaryPatch("right" ).domainCells(), Array3i{ 8,13,18} ) );
	};

	hms::StructMesh mesh = createStructMesh();
	testOneLayer(mesh);
}
